#include "editor_main.h"
#include "editor_brush.h"
#include "editor_spriteset.h"
#include "editor_selection.h"


#include "application/game_script.h"
#include "font/font_manager.h"
#include "ui/ui_manager.h"
#include "dd/dd_man.h"

#include "graphics/graphics_util.h"

#include "util/mmgr.h"		    // Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"		    // logging
#include "util/timer.h"		    // logging


#include <algorithm>
#include <cassert>
using namespace editor;


///////////////////////////////////////////////
int		EditorMain::m_AutoSaveIndex = 0;
int		EditorMain::m_AutoSaveHighest = 0;

static std::string	GetAutoFilename()
{
	char	Buffer[256];
	sprintf(Buffer, "autosave/auto_%03i", EditorMain::m_AutoSaveIndex);
	return Buffer;
}
void	EditorMain::Autosave()
{
	m_pGame->Save(GetAutoFilename());
	m_AutoSaveIndex++;
	m_AutoSaveHighest = m_AutoSaveIndex;
}

void	EditorMain::Undo()
{
	if (m_AutoSaveIndex>=1)
	{
		m_AutoSaveIndex--;
		m_pGame->Load(GetAutoFilename());	
	}
}

void	EditorMain::Redo()
{
	if (m_AutoSaveIndex<m_AutoSaveHighest-1)
	{
		m_AutoSaveIndex++;
		m_pGame->Load(GetAutoFilename());	
	}
}

EditorMain::EditorMain(GameBase* pGame)  :
	m_pGame(pGame),
	m_pBrush(NULL),
	m_SnapToGrid(false),
	m_ShowHelp(true),
	m_RenderDebug(true),
	m_Auto(true),
	m_GridSize(10/800.0f, 10/600.0f),
	m_pSelectionSet(0)
{
	SetHelp("Editor Help [H]:						\n" 
			"(C): Build continuous barrier			\n"
			"(B): Build discreet barrier			\n"
			"(P): Place platforms					\n"
			"(W): Place Waypoint					\n"
			"(S): Place Startpoint					\n"
			"\n"
			"\n"
			"(1): Place ingame decals				\n"
//			"(2): Place character					\n"
			"(3): Place AI							\n"
			"(4): Place Animations					\n"
			"(5): Place FG decals					\n"
			"(6): Place FG animatinos				\n"
			"(9): Place Action Bubbles				\n"
			"(0): Place Interactive Objects	\n"
			"(F9): Save								\n"
			"(F10): Load							\n");
	Autosave();
}

EditorMain::~EditorMain()
{
	UTIL_SAFE_DELETE(m_pBrush);
	UTIL_SAFE_DELETE(m_pSelectionSet);
}

void	EditorMain::Tick(float dt)
{
	if (m_pBrush)
	{
		m_pBrush->Tick(dt);
		if (m_pBrush->IsDone())
		{
			SetBrush(NULL);
		}
	}

	if (m_pSelectionSet)
	{
		m_pSelectionSet->Tick(dt);	
	}

}	

bool	EditorMain::ShowDebug()
{ 
	return m_RenderDebug && (!m_pSelectionSet || !m_pSelectionSet->IsVisible()); 
};
void	EditorMain::Render(const math::Vec2& Offset, float Scale)
{
	if ((m_pSelectionSet && m_pSelectionSet->IsVisible())) return;
	m_Offset = Offset;
	m_Scale = Scale;

	if (m_pBrush) m_pBrush->Render(Offset, Scale);
	if (m_ShowHelp && !(m_pSelectionSet && m_pSelectionSet->IsVisible()) )
	{
		if (m_pBrush)
			FontManager::Instance().Print(math::Vec2(0.5f, 0.1f), m_pBrush->GetHelp().c_str());
		else
			FontManager::Instance().Print(math::Vec2(0.01f, 0.1f), m_HelpText.c_str());

	}
}

void	EditorMain::Init()
{
}

void	EditorMain::Shutdown()
{
}

void	EditorMain::MouseButtonDown(input::Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos)
{
	if (m_Auto)
		MouseButtonReleased(pKey, Pos);

}

void	EditorMain::MouseButtonPressed(input::Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos)
{
	m_MousePos = Pos;
	if (pKey->ButtonID == input::Mouse::BUTTON_LEFT)
	{
		if (m_pSelectionSet && m_pSelectionSet->IsVisible())
		{

		} else
		if (!m_pBrush)
		{

		} else
		{
			m_pBrush->PressLMB();
		} 
	}
}

void	EditorMain::MouseButtonReleased(input::Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos)
{
	m_MousePos = Pos;
	if (ui::UIManager::Instance().MouseInsideActive(Pos)) return;
	if (pKey->ButtonID == input::Mouse::BUTTON_RIGHT)
	{
		Autosave();
		// give the brush a chance to delete stuff
		if (m_pBrush && m_pBrush->TryDelete(Pos))	return;	
		if (CustomDelete(Pos)) return;
	}

	if (pKey->ButtonID == input::Mouse::BUTTON_LEFT)
	{
		if (m_pSelectionSet && m_pSelectionSet->IsVisible())
		{

		} else
		if (m_pBrush)
		{
			m_pBrush->ReleaseLMB();
		} else
		{
			CustomLMB(Pos);
		}
	}
}

void	EditorMain::MouseWheelMove(int WheelInfo, const math::Vec2& Pos)
{
	if (m_pSelectionSet && m_pSelectionSet->IsVisible())
	{
		m_pSelectionSet->Scroll(-WheelInfo);
	} else
	{
		if (m_pBrush) 
			m_pBrush->Rotate(WheelInfo * 5);
	}
	
}

void	EditorMain::MouseMove(const math::Vec2& From, const math::Vec2& To)
{
	math::Vec2	Pos = To;
	m_MousePos = Pos;
	if (m_SnapToGrid)
	{
		math::Vec2	P = Pos + m_Offset;
		int X = int((P.x - m_GridOffset.x) / m_GridSize.x);
		int Y = int((P.y - m_GridOffset.y) / m_GridSize.y);

		Pos.x = m_GridOffset.x + (X * m_GridSize.x) - m_Offset.x;
		Pos.y = m_GridOffset.y + (Y * m_GridSize.y) - m_Offset.y;
	}
	m_MousePos = Pos;
	if (m_pBrush && m_pBrush->ShiftDown())
	{
		m_pBrush->Resize(To-From);
	}
}


void	EditorMain::KeyDown(input::Keyboard::KEY* pKey)
{
	float	Speed = TIMER().Delta()*0.2f;
	switch (pKey->KeyID)
	{
		case KEY_UP:		if (m_pBrush) m_pBrush->Raise(-1); break;
		case KEY_DOWN:		if (m_pBrush) m_pBrush->Raise(1); break;
		case KEY_LEFT:		if (m_pBrush) m_pBrush->Rotate(-1); break;
		case KEY_RIGHT:		if (m_pBrush) m_pBrush->Rotate(1); break;
		case KEY_INSERT:	if (m_pBrush) m_pBrush->AdjustClamp(Speed); break;
		case KEY_DELETE:	if (m_pBrush) m_pBrush->AdjustClamp(-Speed); break;
	}
}

UISpriteSet*	EditorMain::CreateSpriteSelect(EditorBrush*	pBrush, const std::string& Name)
{
	UTIL_SAFE_DELETE(m_pSelectionSet)
	m_pSelectionSet = new UISpriteSet(pBrush, Name);
	m_pSelectionSet->Show(true);
	return (UISpriteSet*)m_pSelectionSet ;
}

TextSelect*	EditorMain::CreateTextSelect(EditorBrush*	pBrush, const std::string& Name)
{
	UTIL_SAFE_DELETE(m_pSelectionSet)
	m_pSelectionSet = new TextSelect(pBrush, Name);
	m_pSelectionSet->Show(true);
	return (TextSelect*)m_pSelectionSet ;
}


void	EditorMain::KeyPressed(input::Keyboard::KEY* pKey)
{
	if (m_pBrush && EditorBrush::CtrlDown())
	{
		switch (pKey->KeyID)
		{
			case KEY_Z:			Undo(); return; break;
			case KEY_X:			Redo(); return; break;
		}
	}
	switch (pKey->KeyID)
	{
		case KEY_RCONTROL:		if (m_pBrush) m_pBrush->ToggleA(); break;
		case KEY_LSHIFT:		if (m_pBrush) m_pBrush->ToggleB(); break;
		case KEY_SPACE:	
			UTIL_SAFE_DELETE(m_pSelectionSet)
			UTIL_SAFE_DELETE(m_pBrush); 
			break;

		case KEY_F9:	
			{
				std::string	File = GameScript::Instance()->GetSaveFileEx("lua", "Save level File");
				if (File.size() > 3)
				{
					GetGame()->Save(File);
				}
			}
			break;
		case KEY_F10:	
			{		
				std::string	File = GameScript::Instance()->GetOpenFileEx("lua", "Load level File");
				if (File.size() > 3)
				{
					GetGame()->Clear();
					GetGame()->Load(File);
				}
			}
			break;

		// general tricks
		case KEY_G:		m_SnapToGrid = !m_SnapToGrid;	break;
		case KEY_H:		m_ShowHelp = !m_ShowHelp;		break;
		case KEY_D:		m_RenderDebug = !m_RenderDebug;		break;

		case KEY_U:		
			{
				ui::UIManager::Instance().SetVisible(!ui::UIManager::Instance().IsVisible());
			}break;
		case KEY_V:		
			{
				if (m_pSelectionSet)
				{
					m_pSelectionSet->ToggleVisible();
				}
			}break;
		default:
				if (EditorBrush* pBrush = CreateBrush(pKey->KeyID))
				{
					SetBrush(pBrush);			
				}
			break;
		
	}
}

void	EditorMain::SetBrush(EditorBrush*	pBrush)
{
//	Autosave();

	m_Auto  = false;
	UTIL_SAFE_DELETE(m_pBrush);
	m_pBrush = pBrush;
}


EditorBrush*	EditorMain::CreateBrush(const std::string& Name)
{
	CreateBrushData*	pBrushDef =	m_BrushCreate.GetElement(Name);
	if (pBrushDef && pBrushDef->pCB)
	{
		EditorBrush*	pBrush = pBrushDef->pCB(this);
		if (pBrush)
		{
			pBrush->SetName(pBrushDef->Name);
		}
		return pBrush;
	}
	return NULL;
}
EditorBrush*	EditorMain::CreateBrush(int	KeyID)
{
	for (unsigned int i=0; i<m_BrushCreate.GetElementC(); i++)
	{
		CreateBrushData*	pBrushDef =	m_BrushCreate.GetElementByIndex(i);
		if (pBrushDef && pBrushDef->pCB && pBrushDef->KeyID == KeyID)
		{
			EditorBrush*	pBrush = pBrushDef->pCB(this);
			if (pBrush)
			{
				pBrush->SetName(pBrushDef->Name);
			}
			return pBrush;
		}
			
	}
	return NULL;
}

void			EditorMain::AddBrush(const std::string& Name, int	KeyID, CreateBrushCB		pCB)
{
	if (m_BrushCreate.GetElement(Name)) return;	// already existing
	CreateBrushData*	pBrushDef = new CreateBrushData;
	pBrushDef->Name = Name;
	pBrushDef->pCB = pCB;
	pBrushDef->KeyID = KeyID;
	m_BrushCreate.AddElement(Name, pBrushDef);
}

